//==================================================================
//  Copyright (C) 2006  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
/// @file m5d_displist.h
///
/// Display-list class.
///
//==================================================================

#ifndef M5D_DISPLIST_H
#define M5D_DISPLIST_H

#include "pvector.h"
#include "m5d_verts.h"
#include "m5d_triangle.h"
#include "m5d_displist_vpool.h"

//==================================================================
struct VMERGE_t;

//==================================================================
namespace M5D {

//==================================================================
/// 
//==================================================================
namespace Primitive
{
	enum Type {
		TYPE_LINELIST,
		TYPE_TRIANGLELIST,
		TYPE_QUADLIST,
		TYPE_N
	};

#pragma pack(push,2)

	//==================================================================
	struct LineList 
	{
		union {
			struct
			{
				u_short	a ,b;
			};
			u_short	v[2];
		};
	};

	struct TriangleList 
	{
		union {
			struct
			{
				u_short	a ,b, c;
			};
			u_short	v[3];
		};
	};

	struct QuadList 
	{
		union {
			struct
			{
				u_short	a ,b, c, d;
			};
			u_short	v[4];
		};
	};

#pragma pack(pop)
};

//==================================================================
//==
//==================================================================
class DispListBase : public PSYS::sync_obj
{
public:
	DListVertPool	_vert_def;
	void			*_userdatap;

public:
	//==================================================================
	DispListBase( PackVert::Type packvert_type=PackVert::TYPE_ANY, int max_verts=0 );
	DispListBase( const DispListBase *src_displistbasep );

	~DispListBase()
	{
	}

	void	*BeginVerts( int from_idx=0, int n_verts=0 )
			{
				AssertLockW();
				_vert_def.LockRW( __FILE__, __LINE__ );
				return _vert_def.GetVertPtr( from_idx, VType::POS );
			}

	void	EndVerts()
			{
				_vert_def.Unlock();
			}

	u_int	GetMaxVerts() const		{	return _vert_def._n_verts; }
	u_int	GetVertStride() const	{	return _vert_def._stride; }

	void	ReallocPackVerts( PackVert::Type packvert_type, int max_verts ) throw(...);

	bool	HasCol() const	{	return _vert_def.IsActiveUsage( VType::COL );	}
	bool	HasTex() const	{	return _vert_def.IsActiveUsage( VType::TEX );	}
	bool	HasNor() const	{	return _vert_def.IsActiveUsage( VType::NOR );	}

protected:
//	void	allocVerts( int verts_n ) throw(...);
};

//==================================================================
/// Indexed primitives display-list class.
//==================================================================
class DispList : public DispListBase
{
	class Scene	*_scenep;
public:
	//==================================================================
	struct Pool
	{
		Material		*_pool_materialp;
		Primitive::Type	_pool_prim_type;
		int				_pool_n_verts;
		int				_pool_prims_offset;

		Material		*GetMaterial()		{	return _pool_materialp;		}
		const Material	*GetMaterial() const{	return _pool_materialp;		}
		Primitive::Type	GetPrimType() const	{	return _pool_prim_type;		}
		int				GetVertsN() const	{	return _pool_n_verts;		}
		int				GetPrimsN() const
						{	
							switch ( _pool_prim_type )
							{
							case Primitive::TYPE_TRIANGLELIST:	return _pool_n_verts / 3;
							case Primitive::TYPE_LINELIST:		return _pool_n_verts / 2;
							case Primitive::TYPE_QUADLIST:		return _pool_n_verts / 4;
							default:							PASSERT( 0 );	return 0;	// not good
							}
						}
		int				GetOffset()	const {	return _pool_prims_offset;	}
	};

	static const int	MAX_PRIMS_POOLS	= 64;

private:
	//==================================================================
	u_short		*_prim_indices_datap;
	int			_max_prim_indices;
	Pool		_pools[MAX_PRIMS_POOLS];
	int			_n_pools;
	int			_max_used_verts;

private:
	DispList( class Scene *scenep, PackVert::Type packvert_type=PackVert::TYPE_ANY, int max_verts=0, u_int max_prim_indices=0 );
	DispList( class Scene *scenep, const DispList *src_displistp );

public:
	~DispList()
	{
		freeDList();
	}

	void		BuildVtxFromIdx( const DispList *src_idxlistp ) throw(...);

	//--------------------------------------------------------------
private:
	void		freeDList();
	Pool		*AddPrimPool( Material *matep, Primitive::Type prim_type, int n_prim_verts );
	Pool		*addPrimPoolRaw( Material *matep, Primitive::Type prim_type, int n_prim_verts );
	Pool		*FindPrimPool( Material *matep, Primitive::Type prim_type );
	int			FindPrimPoolIdx( Material *matep, Primitive::Type prim_type );
	void		refreshPoolPtrs();
	void		AllocIndices( int n_prim_indices ) throw(...);
public:
	bool		IsIndexType() const				{ return _max_prim_indices > 0;	}
	u_int		GetMaxUsedVerts() const			{ return _max_used_verts;	}
	u_int		GetMaxUsedPrimVerts() const
				{
					if ( _n_pools <= 0 )
						return 0;
					else
						return _pools[ _n_pools-1 ]._pool_prims_offset + _pools[ _n_pools-1 ]._pool_n_verts;
				}

	u_int		GetMaxUsedIndices() const
				{
					if ( IsIndexType() )
						return GetMaxUsedPrimVerts();
					else
						return 0;
				}

	u_int		GetMaxIndices() const			{ return _max_prim_indices;	}
	u_int		GetPrimPoolsN() const			{ return _n_pools;			}
	Pool		*GetPrimPool( int idx )			{ return &_pools[idx];		}
	const Pool	*GetPrimPool( int idx ) const	{ return &_pools[idx];		}
	u_short		*GetFirstIndexPtr()				{ return _prim_indices_datap;	}
	const u_short	*GetFirstIndexPtr()	const	{ return _prim_indices_datap;	}

	void		Reset()
				{
					AssertLockW();
					_n_pools = 0;
					_max_used_verts = 0;
				}

	//==================================================================
	u_short		*BeginAddIndexPrims( Material *matep, Primitive::Type prim_type, int n_prim_verts=0 );
	void		EndAddIndexPrims();
	void 		*BeginAddVertexPrims( Material *matep, Primitive::Type prim_type, int n_prim_verts=0 );
	void		EndAddVertexPrims( int updated_n_prim_verts=-1 );

	PackVert::PosTexColNor	*BeginAddVertexPrimsPTCN( Material *matep, Primitive::Type prim_type, int n_prim_verts=0 )
	{
		return (PackVert::PosTexColNor *)BeginAddVertexPrims( matep, prim_type, n_prim_verts );
	}
	PackVert::PosTexCol		*BeginAddVertexPrimsPTC( Material *matep, Primitive::Type prim_type, int n_prim_verts=0 )
	{
		return (PackVert::PosTexCol *)BeginAddVertexPrims( matep, prim_type, n_prim_verts );
	}
	PackVert::PosTexNor		*BeginAddVertexPrimsPTN( Material *matep, Primitive::Type prim_type, int n_prim_verts=0 )
	{
		return (PackVert::PosTexNor *)BeginAddVertexPrims( matep, prim_type, n_prim_verts );
	}
	PackVert::PosColNor		*BeginAddVertexPrimsPCN( Material *matep, Primitive::Type prim_type, int n_prim_verts=0 )
	{
		return (PackVert::PosColNor *)BeginAddVertexPrims( matep, prim_type, n_prim_verts );
	}
	PackVert::PosTex		*BeginAddVertexPrimsPT( Material *matep, Primitive::Type prim_type, int n_prim_verts=0 )
	{
		return (PackVert::PosTex *)BeginAddVertexPrims( matep, prim_type, n_prim_verts );
	}
	PackVert::PosCol		*BeginAddVertexPrimsPC( Material *matep, Primitive::Type prim_type, int n_prim_verts=0 )
	{
		return (PackVert::PosCol *)BeginAddVertexPrims( matep, prim_type, n_prim_verts );
	}
	PackVert::PosNor		*BeginAddVertexPrimsPN( Material *matep, Primitive::Type prim_type, int n_prim_verts=0 )
	{
		return (PackVert::PosNor *)BeginAddVertexPrims( matep, prim_type, n_prim_verts );
	}
	PackVert::Pos			*BeginAddVertexPrimsP( Material *matep, Primitive::Type prim_type, int n_prim_verts=0 )
	{
		return (PackVert::Pos *)BeginAddVertexPrims( matep, prim_type, n_prim_verts );
	}

public:
	//==================================================================
	void	RebuildIdxDList(	Triangles &trigs, int n_trigs,
								const PMath::Vector3	*src_posp, u_int pos_out_vf,
								const PMath::Vector2	*src_texp, u_int tex_out_vf,
								const PSYSGFX::Color4b	*src_colp, u_int col_out_vf,
								const PMath::Vector3	*src_norp, u_int nor_out_vf ) throw(...);

	void	RebuildVtxDList(	Triangles &trigs, int n_trigs,
								const PMath::Vector3	*src_posp, u_int pos_out_vf,
								const PMath::Vector2	*src_texp, u_int tex_out_vf,
								const PSYSGFX::Color4b	*src_colp, u_int col_out_vf,
								const PMath::Vector3	*src_norp, u_int nor_out_vf,
								const bool *foced_vtypesp=NULL ) throw(...);

	void	RebuildOnOffDList(	Triangles &trigs,
								const u_char *tri_onoffmap,
								int idx_base,
								u_int col_on, u_int col_off,
								const PMath::Vector3	*src_posp,
								const PMath::Vector3	*src_norp ) throw(...);

	void	UpdateOnOffDList( const u_char *tri_onoffmap, int idx_base, u_int col_on, u_int col_off );

	void	RebuildTriIDDList(	Triangles &trigs, u_int start_id, const PMath::Vector3 *src_posp ) throw(...);

	void	RebuildPosOnlyDList(	Triangles &trigs, const PMath::Vector3 *src_posp, int n_verts ) throw(...);

private:
	void	rebuildCreateTrigs( const Triangle *trigs_list_frontp, int n_trigs, struct VMERGE_t *vmergep ) throw(...);
	void	setupPoolsFromTriangles( const Triangle *trigs_list_frontp, int n_trigs ) throw(...);

	int		rebuild_add_vertices_triangles( const Triangle *trigs_list_frontp, u_int n_trigs,
											bool has_tex,
											bool has_col,
											bool has_nor,
											struct VMERGE_t *vmergep ) throw(...);
	//--------------------------------------------------------------
	void	rebuildCreateVerts(	u_char			*src_datap[4],
								const u_int		src_format[4],
								struct VMERGE_t	*vmergep ) throw(...);

	void	rebuildVertsForVtxlist(
						u_char				*src_datap[4],
						const u_int			src_format[4],
						const Triangle	*trigs_list_frontp,
						int					n_trigs );

	friend class Scene;
};

};

#endif